/*
 * TCSS 422 Web Spider Project
 * Group Money: Al McKenzie, Michael Pitts, Taylor Zielske
 */
package control.data;

import java.io.PrintStream;
import java.util.Observable;

/**
 * This class stores the results of the URL searches and signals when the program
 * is done searching.  It also notifies it's observers with search results.
 * 
 * @author Michael Pitts
 * @version Oct 26, 2011
 */
public class SearchHopper extends Observable {
	
	/**
	 * Indicates the program should stop.
	 */
	public static final int STOP = 1;

	/**
	 * Stores the words being searched on.
	 */
	private final String[] my_words;
	
	/**
	 * Stores how many urls to search on.
	 */
	private final int my_max_sites;
	
	/**
	 * The frequency of each word over all the searches.
	 */
	private final int[] my_frequencies;
	
	/**
	 * How many sites have been searched.
	 */
	private int my_sites_searched;
	
	/**
	 * The streams to print output to.
	 */
	private final PrintStream[] my_streams;
	
	/**
	 * How frequently the data update is sent.
	 */
	private final int my_update_frequency;

	/**
	 * The start time.
	 */
	private final long my_start_time;
	
	/**
	 * Total number of words found in the search.
	 */
	private long my_total_words;

	/**
	 * Total number of urls found in the search.
	 */
	private int my_urls_found;
	
	/**
	 * Creates a new SearchHopper that store the results of url searches.
	 * @param the_words are the words to search on.
	 * @param the_max_sites is the maximum number of sites to search.
	 * @param the_streams are the streams to print data to, can be null if no 
	 * file or console output is desired.
	 * @param the_update_frequency is how often to update the gui.
	 */
	public SearchHopper(final String[] the_words, final int the_max_sites, 
			final PrintStream[] the_streams, final int the_update_frequency) {
		my_words = the_words.clone();
		my_max_sites = the_max_sites;
		my_frequencies = new int[the_words.length];
		my_update_frequency = the_update_frequency;
		my_sites_searched = 0;
		my_total_words = 0;
		my_urls_found = 0;
		my_streams = the_streams;
		my_start_time = System.currentTimeMillis();
	}

	/**
	 * Adds the given results to the running total.
	 * @param the_frequencies how frequently each word appeared.
	 * @param the_url the url just searched.
	 * @param the_num_urls is the number of urls found on this page.
	 */
	public synchronized void addResults(final int[] the_frequencies, final String the_url, 
			final int the_num_urls) {
		my_sites_searched++;
		if (my_sites_searched < my_max_sites) {
			my_urls_found += the_num_urls;
			for (int i = 0; i < my_frequencies.length; i++) {
				my_frequencies[i] += the_frequencies[i];
				my_total_words += the_frequencies[i];
			}
			runUpdate(the_url);
		}else if (my_sites_searched == my_max_sites){
			for (int i = 0; i < my_frequencies.length; i++) {
				my_frequencies[i] += the_frequencies[i];
				my_total_words += the_frequencies[i];
			}
			runUpdate(the_url);
			dataUpdate(the_url);
			done();
		}
	}
	
	/**
	 * Runs an update for the most recent url searched, outputting the data to 
	 * PrintStreams and the gui.
	 * @param the_url is the most recently searched url.
	 */
	private void runUpdate(final String the_url) {
		if (my_sites_searched % my_update_frequency == 0) {
			dataUpdate(the_url); // updates the gui and control periodically
		}
		if (my_streams != null) {
			for (PrintStream stream : my_streams) {
				print(stream, the_url);
			}
		}
	}
	
	/**
	 * Sends out a large data update for the gui.
	 * @param the_url is the url most recently searched.
	 */
	private void dataUpdate(final String the_url) {
		setChanged();
		final Object[] data = {the_url, my_sites_searched, my_total_words, my_urls_found,
				my_start_time, my_max_sites, my_words.clone(), my_frequencies.clone()};
		notifyObservers(data);
	}

	/**
	 * Prints a text output to a single given PrintStream.
	 * @param the_stream is the stream to print to.
	 * @param the_url is the url that was most recently searched.
	 */
	private void print(final PrintStream the_stream, final String the_url) {
		final long now = System.currentTimeMillis();
		the_stream.println("Parsed: " + the_url);
		the_stream.println("Pages Retrieved: " + my_sites_searched);
		the_stream.println("Average words per page: " + 
				(float) my_total_words / my_sites_searched);
		the_stream.println("Average URLs per page: " + 
				(float) my_urls_found / my_sites_searched);
		the_stream.println("Keyword\t\tAve. hits per page\tTotal hits");
		for (int i = 0; i < my_words.length; i++) {
			the_stream.print(my_words[i]);
			the_stream.print("\t");
			if (my_words[i].length() < 8) { // padding to keep nice columns
				the_stream.print("\t");
			}
			the_stream.println((float) my_frequencies[i] / my_sites_searched + 
					"\t\t\t" + my_frequencies[i]);
		}
		the_stream.println();
		the_stream.println("Page limit: " + my_max_sites);
		the_stream.println("Average time per page " + 
				(now - my_start_time) / my_sites_searched + " msec");
		the_stream.println("Total running time: " + 
				(float) (now - my_start_time) / 1000 + " sec");
		the_stream.println();
		the_stream.println();
	}

	/**
	 * Run when the max number of sites has been searched.
	 */
	private void done() {
		setChanged();
		notifyObservers(new Integer(STOP));
	}
}
